home *** CD-ROM | disk | FTP | other *** search
- //
- // ThreadedServer.m
- // SchmoozingExamples
- //
- // Created by garrison on Fri Apr 20 2001.
- // Copyright (c) 2001 Standard Orbit Software, LLC. All rights reserved.
- //
- // Permission is granted to use this code for any purpose, at your own risk.
- // No warranties are expressed or implied.
- //
-
- /* An Algorithm for a Concurrent Server [Comer93]
-
- Master 1) Create a socket for the server and bind it to the well known port
- for the service being offered.
- Master 2) Establishing a listener on the socket.
- Master 3) Repeatedly accept incoming requests and create new slaves to handle the response.
- Slave 1) Receive a connection request/socket upon creation.
- Slave 2) Interact with the client using the socket, reading requests and sending back replies.
- Slave 3) Close the connection and exit. The slave exits after handling all requests
- from one client.
- */
-
- #import "ThreadedServer.h"
-
- #import <OmniNetworking/OmniNetworking.h>
- #import "Connection.h"
-
- @implementation ThreadedServer
-
- - (id) initWithLocalPort:(unsigned short) aPort
- {
- self = [super init];
-
- if ( self )
- {
- NS_DURING {
-
- listeningSocket = [[ONTCPSocket tcpSocket] retain];
- // Master Step 1. Create an ONTCPSocket for the server.
-
- [listeningSocket startListeningOnLocalPort:aPort allowingAddressReuse:YES];
- // Master Step 2.
- // Start socket listening on the well known port. Allowing address reuse is
- // helpful while debugging (it prevents a port from getting locked up if your
- // app crashes without closing the socket).
-
- NSLog(@"Listening for connections on host %@:%d", [ONHost localHostname], aPort);
- }
- NS_HANDLER {
- // Handle potential exceptions.
- NSLog(@"%@ raised while starting the server: %@",
- [localException name], localException);
- [listeningSocket release];
- return nil;
- }
- NS_ENDHANDLER
- }
-
- return self;
- }
-
-
- - (void) dealloc
- {
- [listeningSocket release];
- [super dealloc];
- }
-
-
- - (void) run
- {
- // This method implements a concurrent server using threads. A new client
- // connection object is created for each accepted connection. A new
- // thread is detached to then run the client connection's processing method.
-
- ONTCPSocket *connectionSocket = nil;
- Connection *someClient = nil;
- unsigned short connectionCount = 0;
-
- // 'connectionSocket' will hold the accepted connection, leaving
- // the listening socket free to continue listening.
- // 'someClient' is an object of our own custom class for handling
- // connections to our server.
- // 'connectionCount' holds a count of connections accepted, just to
- // provide some feedback in the spartan UI.
-
- NSLog(@"Threaded Concurrent Server");
-
- do
- {
- NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
-
- NS_DURING
- {
- NSLog(@"Main thread listening for next connection");
-
- connectionSocket = [listeningSocket acceptConnectionOnNewSocket];
- // Master Step 3. Accept the next incoming connection. The main
- // thread will block until a connection request is received.
-
- connectionCount += 1;
- // Increment the accepted connection count
-
- NSLog(@"Accepted connection %d from host %@", connectionCount,
- [[connectionSocket remoteAddressHost] hostname]);
-
- someClient = [[Connection alloc] initWithConnectedSocket:connectionSocket];
- // Slave Step 1. Receive the accepted connection when our slave is
- // initialized. The role of Slave is played by 'someClient'.
-
- NSLog(@"Detaching a thread to process the connection");
- [NSThread detachNewThreadSelector:@selector(processConnection)
- toTarget:someClient withObject:nil];
- // Slave Step 2. Spawn off a new thread to run our server's interaction
- // with the client.
-
- [loopPool release];
- // Since detachNewThreadSelector: will retain our Connection object,
- // (and Connection retained the accepted socket object on init), we can
- // safely release the loop pool (thereby removing our responsibility
- // for these objects). When the thread terminates, those objects
- // will be released (and the socket will be closed).
- }
-
- NS_HANDLER
- {
- NSLog(@"%@ raised during threaded server processing: %@",
- [localException name], localException);
- [loopPool release];
- break;
- // Close down the server loop if an error occurs.
- }
- NS_ENDHANDLER
-
- } while (1);
- // Loop forever listening for and accepting connections. As implemented here, the only way
- // the server will quit is if an exception is raised during execution. You will want to add
- // some additional means of gracefully bringing a server down (e.g. handling
- // a SIGTERM or SIGHUP signal).
- }
- @end
-